package cn.coisini.model.common.aspect;

import cn.coisini.model.common.annotation.RedisAccessLimit;
import cn.coisini.model.common.dtos.Result;
import cn.coisini.model.common.enums.ResultEnum;
import cn.coisini.utils.IpUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import static cn.coisini.utils.JwtUtil.getUserIdFromToken;

/**
 * @author xiangshaw
 * Description: redis限流切面，拦截注解 RedisAccessLimit 标注的方法，使用 Redis 实现限流逻辑。
 */
@Aspect
@Component
@RequiredArgsConstructor
public class RedisAccessLimitAspect {

    private final StringRedisTemplate stringRedisTemplate;
    private final HttpServletRequest request;

    @Around("@annotation(cn.coisini.model.common.annotation.RedisAccessLimit)")
    public Object accessLimit(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名和目标方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        // 获取注解
        RedisAccessLimit redisAccessLimit = method.getAnnotation(RedisAccessLimit.class);
        if (redisAccessLimit == null) {
            return joinPoint.proceed();
        }

        // 获取注解参数
        int seconds = redisAccessLimit.seconds();
        int maxCount = redisAccessLimit.maxCount();
        String message = redisAccessLimit.message();

        // 构造 Redis Key （cn.coisini.controller.v1.ArticleViewController:getArticleViewList:127.0.0.1）
        String key = getKey(joinPoint);

        // 从 Redis 获取当前访问次数
        String count = stringRedisTemplate.opsForValue().get(key);
        int currentCount = count == null ? 0 : Integer.parseInt(count);

        // 检查是否超过最大访问次数
        if (currentCount >= maxCount) {
            return Result.error(ResultEnum.ACCESS_LIMIT.getCode(), message);
        }

        // 增加访问次数并设置过期时间
        if (currentCount == 0) {
            stringRedisTemplate.opsForValue().set(key, "1", seconds, TimeUnit.SECONDS);
        } else {
            stringRedisTemplate.opsForValue().increment(key);
        }

        // 执行目标方法
        return joinPoint.proceed();
    }

    // 生成 Redis Key
    private String getKey(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getName();
        String getUserId = getUserId();
        return className + ":" + methodName + ":" + getUserId;
    }

    // 获取用户 ID
    private String getUserId() {
        // 获取当前用户 ID
        String userId = getUserIdFromToken();
        if (userId != null) {
            return userId;
        }
        // 如果没有用户信息，使用 IP 地址作为标识
        return IpUtils.getIp(request);
    }
}
